package jmine.tec.extensions.impl.forms.server;

import static jmine.tec.extensions.impl.TecExtensionsMessages.ERROR_CREATING_SERVICE_FOR_FORM;
import static jmine.tec.extensions.impl.TecExtensionsMessages.ERROR_EXECUTING_SERVICE_FOR_FORM;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import jmine.tec.extensions.TecExtensionsException;
import jmine.tec.extensions.forms.FormHandler;
import jmine.tec.extensions.forms.FormHandlerHelper;
import jmine.tec.services.ServicesController;
import jmine.tec.services.api.ServiceExecutionException;
import jmine.tec.services.api.ServiceProperties;
import jmine.tec.services.api.ServicesPage;
import jmine.tec.services.api.execution.ExecutorParams;
import jmine.tec.services.api.execution.ServicesPageExecutor;
import jmine.tec.services.api.export.EntityExporter;
import jmine.tec.services.api.factory.ServiceCreationException;
import jmine.tec.services.api.factory.ServiceFactory;
import jmine.tec.services.export.impl.ReferenceMapImpl;
import jmine.tec.services.io.ServiceBean;
import bancosys.tec.exception.CompoundMessageHolder;
import bancosys.tec.exception.LocalizedMessageHolder;
import bancosys.tec.exception.informer.UserFriendlyExceptionInformer;

/**
 * @author wagner.tsuchiya
 * @param <T> form's type.
 */
public abstract class AbstractServiceGeneratingFormHandler<T> implements FormHandler<T> {

    private final Class<T> formClass;

    private ServicesController servicesController;

    private ServiceFactory serviceFactory;

    private ServicesPageExecutor servicesPageExecutor;

    private UserFriendlyExceptionInformer userFriendlyExceptionInformer;

    /**
     * @param formClass the form class.
     */
    public AbstractServiceGeneratingFormHandler(Class<T> formClass) {
        this.formClass = formClass;
    }

    /**
     * {@inheritDoc}
     */
    public void apply(T form) {
        FormHandlerHelper.validateThrowException(form, this);
        try {
            this.execute(form);
        } catch (ServiceCreationException e) {
            throw new TecExtensionsException(ERROR_CREATING_SERVICE_FOR_FORM.create(new CompoundMessageHolder()
                    .addMessages(this.userFriendlyExceptionInformer.getUserFriendlyMessages(e))), e);
        } catch (ServiceExecutionException e) {
            throw new TecExtensionsException(ERROR_EXECUTING_SERVICE_FOR_FORM.create(new CompoundMessageHolder().addMessages(this
                    .serviceExecutionErrors(e))), e);
        }
    }

    /**
     * Executes a form
     * 
     * @param form form
     * @throws ServiceCreationException ServiceCreationException
     * @throws ServiceExecutionException ServiceExecutionException
     */
    protected void execute(T form) throws ServiceCreationException, ServiceExecutionException {
        ServicesPage services = new ServicesPage();
        List<ServiceBean> serviceBeans = this.getExporter().export(new ReferenceMapImpl(), form);
        ServiceProperties parentProperties = new ServiceProperties(this.servicesController);
        for (ServiceBean serviceBean : serviceBeans) {
            services.addService(this.serviceFactory.getService(serviceBean.getAction(), serviceBean.getName(), parentProperties));
        }
        ExecutorParams executorParams = new ExecutorParams();
        this.servicesPageExecutor.execute(Collections.singletonList(services), executorParams);
    }

    /**
     * Finds all error messages abount a service execution exception
     * 
     * @param e exception
     * @return list of error messages
     */
    protected List<LocalizedMessageHolder> serviceExecutionErrors(ServiceExecutionException e) {
        List<LocalizedMessageHolder> errors = new ArrayList<LocalizedMessageHolder>();
        for (Throwable cause : e.getCauses()) {
            errors.addAll(this.userFriendlyExceptionInformer.getUserFriendlyMessages(cause));
        }
        return errors;
    }

    /**
     * {@inheritDoc}
     */
    public Class<T> formClass() {
        return this.formClass;
    }

    /**
     * The exporter to be used to apply the form.
     * 
     * @return the exporter.
     */
    protected abstract EntityExporter<T> getExporter();

    /**
     * @param servicesController the servicesController to set
     */
    public void setServicesController(ServicesController servicesController) {
        this.servicesController = servicesController;
    }

    /**
     * @param serviceFactory the serviceFactory to set
     */
    public void setServiceFactory(ServiceFactory serviceFactory) {
        this.serviceFactory = serviceFactory;
    }

    /**
     * @param servicesPageExecutor the servicesPageExecutor to set
     */
    public void setServicesPageExecutor(ServicesPageExecutor servicesPageExecutor) {
        this.servicesPageExecutor = servicesPageExecutor;
    }

    /**
     * @param userFriendlyExceptionInformer the userFriendlyExceptionInformer to set
     */
    public void setUserFriendlyExceptionInformer(UserFriendlyExceptionInformer userFriendlyExceptionInformer) {
        this.userFriendlyExceptionInformer = userFriendlyExceptionInformer;
    }
}
